home *** CD-ROM | disk | FTP | other *** search
/ EnigmA Amiga Run 1995 October / EnigmA AMIGA RUN 01 (1995)(G.R. Edizioni)(IT)[!][issue 1995-10][Aminet 7].iso / Aminet / comm / tcp / AmigaTCP.lha / AmigaTCP / src / smtpserv.c < prev    next >
C/C++ Source or Header  |  1989-06-24  |  9KB  |  415 lines

  1. /* SMTP Server state machine - see RFC 821
  2.  * Very simple implementation; no forwarding allowed
  3.  * (who wants to re-create "sendmail" ??)
  4.  */
  5. #include <stdio.h>
  6. #include "machdep.h"
  7. #include "mbuf.h"
  8. #include "netuser.h"
  9. #include "timer.h"
  10. #include "tcp.h"
  11. #include "smtp.h"
  12. #ifdef LATTICE
  13. #define tmpfile(x) tmpnam("testingxxxxxxxxxx")
  14. #endif
  15.  
  16. /* Command table */
  17. static char *commands[] = {
  18.     "helo",
  19. #define    HELO_CMD    0
  20.     "noop",
  21. #define    NOOP_CMD    1
  22.     "mail from:",
  23. #define    MAIL_CMD    2
  24.     "quit",
  25. #define    QUIT_CMD    3
  26.     "rcpt to:",
  27. #define    RCPT_CMD    4
  28.     "help",
  29. #define    HELP_CMD    5
  30.     "data",
  31. #define    DATA_CMD    6
  32.     "rset",
  33. #define    RSET_CMD    7
  34.     NULLCHAR
  35. };
  36.  
  37. /* Reply messages */
  38. static char help[] = "214-Commands:\r\n214-HELO NOOP MAIL QUIT RCPT HELP DATA RSET\r\n214 End\r\n";
  39. static char banner[] = "220 %s SMTP Ready\r\n";
  40. static char closing[] = "221 Closing\r\n";
  41. static char ok[] = "250 Ok\r\n";
  42. static char reset[] = "250 Reset state\r\n";
  43. static char sent[] = "250 Sent\r\n";
  44. static char ourname[] = "250 %s, Share and Enjoy!\r\n";
  45. static char enter[] = "354 Enter mail, end with .\r\n";
  46. static char ioerr[] = "452 Temp file write error\r\n";
  47. static char mboxerr[] = "452 Mailbox %s write error\r\n";
  48. static char badcmd[] = "500 Command unrecognized\r\n";
  49. static char syntax[] = "501 Syntax error\r\n";
  50. static char needrcpt[] = "503 Need RCPT (recipient)\r\n";
  51. static char badname[] = "550 Can't open mailbox for %s\r\n";
  52. static struct tcb *smtp_tcb;
  53. /* Start up SMTP receiver service */
  54. smtp_start(argc,argv)
  55. int argc;
  56. char *argv[];
  57. {
  58.     struct socket lsocket;
  59.     void r_mail(),s_mail();
  60.  
  61.     lsocket.address = ip_addr;
  62.     if(argc < 2)
  63.         lsocket.port = SMTP_PORT;
  64.     else
  65.         lsocket.port = atoi(argv[1]);
  66.  
  67.     smtp_tcb = open_tcp(&lsocket,NULLSOCK,
  68.         TCP_PASSIVE,0,r_mail,NULLVFP,s_mail,0,(int *)NULL);
  69. }
  70.  
  71. /* Shutdown SMTP service (existing connections are allowed to finish) */
  72. smtp_stop()
  73. {
  74.     if(smtp_tcb != NULLTCB)
  75.         close_tcp(smtp_tcb);
  76. }
  77. /* SMTP connection state change upcall handler */
  78. static void
  79. s_mail(tcb,old,new)
  80. struct tcb *tcb;
  81. char old,new;
  82. {
  83.     struct mail *mp,*mail_create();
  84.  
  85.     switch(new){
  86. #ifdef    QUICKSTART
  87.     case SYN_RECEIVED:
  88. #else
  89.     case ESTABLISHED:
  90. #endif
  91.         if((mp = mail_create(tcb)) == NULLMAIL){
  92.             close_tcp(tcb);
  93.             break;
  94.         }
  95.         tprintf(mp->tcb,banner,hostname);
  96.         log(tcb,"open SMTP");
  97.         break;        
  98.     case CLOSE_WAIT:
  99.         mp = (struct mail *)tcb->user;
  100.         mail_delete(mp);                
  101.         close_tcp(tcb);
  102.         break;
  103.     case CLOSED:
  104.         log(tcb,"close SMTP");
  105.         del_tcp(tcb);
  106.         /* Check if server is being shut down */
  107.         if(tcb == smtp_tcb)
  108.             smtp_tcb = NULLTCB;
  109.         break;
  110.     }
  111. }
  112.  
  113. /* SMTP receiver upcall handler */
  114. static void
  115. r_mail(tcb,cnt)
  116. struct tcb *tcb;
  117. int16 cnt;
  118. {
  119.     register struct mail *mp;
  120.     char *index(),*inet_ntoa(),c;
  121.     struct mbuf *bp;
  122.     void docommand(),deliver(),doline();
  123.  
  124.     if((mp = (struct mail *)tcb->user) == NULLMAIL){
  125.         /* Unknown session */
  126.         close_tcp(tcb);
  127.         return;
  128.     }
  129.     recv_tcp(tcb,&bp,cnt);
  130.     /* Assemble an input line in the session buffer.
  131.      * Return if incomplete
  132.      */
  133.     while(pullup(&bp,&c,1) == 1){
  134.         switch(c){
  135.         case '\r':    /* Strip cr's */
  136.             continue;
  137.         case '\n':    /* Complete line; process it */
  138.             mp->buf[mp->cnt] = '\0';
  139.             doline(mp);
  140.             break;
  141.         default:    /* Assemble line */
  142.             mp->buf[mp->cnt++] = c;
  143.             break;
  144.         }
  145.     }
  146. }
  147. /* Process a line read on an SMTP connection (any state) */
  148. static
  149. void
  150. doline(mp)
  151. register struct mail *mp;
  152. {
  153.     switch(mp->state){
  154.     case COMMAND_STATE:
  155.         docommand(mp);
  156.         break;
  157.     case DATA_STATE:
  158.         tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  159.         if(mp->buf[0] == '.' && strlen(mp->buf) == 1){
  160.             fprintf(mp->data,"\n");    /* Leave a blank line between msgs */
  161.  
  162.             mp->state = COMMAND_STATE;
  163.             deliver(mp);    /* Also sends appropriate response */
  164.             break;
  165.         }
  166.         /* Append to data file */
  167.         if(fprintf(mp->data,"%s\n",mp->buf) < 0){
  168.             mp->state = COMMAND_STATE;
  169.             tprintf(mp->tcb,ioerr);
  170.         }
  171.         break;
  172.     }
  173.     mp->cnt = 0;
  174. }
  175. /* Create control block, initialize */
  176. static struct mail *
  177. mail_create(tcb)
  178. register struct tcb *tcb;
  179. {
  180.     register struct mail *mp;
  181.     char *calloc();
  182.  
  183.     if((mp = (struct mail *)calloc(1,sizeof (struct mail))) == NULLMAIL){
  184.         return NULLMAIL;
  185.     }
  186.     mp->tcb = tcb;        /* Downward pointer */
  187.     tcb->user = (int *)mp;    /* Upward pointer */
  188.     return mp;
  189. }
  190.  
  191. /* Free resources, delete control block */
  192. static
  193. mail_delete(mp)
  194. register struct mail *mp;
  195. {
  196.     register struct addr *ap,*ap1;
  197.  
  198.     if(mp->system != NULLCHAR)
  199.         free(mp->system);
  200.     for(ap = mp->to;ap != NULLADDR;ap = ap1){
  201.         if(ap->val != NULLCHAR)
  202.             free(ap->val);
  203.         ap1 = ap->next;
  204.         free((char *)ap);
  205.     }
  206.     if(mp->data != NULLFILE)
  207.         fclose(mp->data);
  208.     free((char *)mp);
  209. }
  210.  
  211. /* Parse and execute mail commands */
  212. static
  213. void
  214. docommand(mp)
  215. register struct mail *mp;
  216. {
  217.     char mailbox[50];
  218.     char *cmd,*arg,*cp,*cp1,**cmdp;
  219.     char *index(),*malloc(),*getname();
  220.     struct tcb *tcb;
  221.     struct addr *ap;
  222. #ifdef LATTICE
  223.     FILE *fp;
  224. #else
  225.     FILE *tmpfile(),*fp;
  226. #endif
  227. #ifdef    DATE
  228.     long t;
  229.     char *ctime();
  230. #endif
  231.  
  232.     cmd = mp->buf;
  233.     if(mp->cnt < 4){
  234.         /* Can't be a legal SMTP command */
  235.         tprintf(mp->tcb,badcmd);
  236.         return;
  237.     }    
  238.     cmd = mp->buf;
  239.  
  240.     /* Translate entire buffer to lower case */
  241.     for(cp = cmd;*cp != '\0';cp++)
  242.         *cp = tolower(*cp);
  243.  
  244.     /* Find command in table; if not present, return syntax error */
  245.     for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
  246.         if(strncmp(*cmdp,cmd,strlen(*cmdp)) == 0)
  247.             break;
  248.     if(*cmdp == NULLCHAR){
  249.         tprintf(mp->tcb,badcmd);
  250.         return;
  251.     }
  252.     arg = &cmd[strlen(*cmdp)];
  253.     /* Skip spaces after command */
  254.     while(*arg == ' ')
  255.         *arg++;
  256.     /* Execute specific command */
  257.     switch(cmdp-commands){
  258.     case HELO_CMD:
  259.         if((mp->system = malloc((unsigned)strlen(arg)+1)) == NULLCHAR){
  260.             /* If the system is out of memory, just close */
  261.             close_tcp(mp->tcb);
  262.             mail_delete(mp);
  263.             break;            
  264.         } else {
  265.             strcpy(mp->system,arg);
  266.             tprintf(mp->tcb,ourname,hostname);
  267.         }
  268.         break;
  269.     case NOOP_CMD:
  270.     case MAIL_CMD:    /* Rather useless */
  271.         tprintf(mp->tcb,ok);
  272.         break;
  273.     case QUIT_CMD:
  274.         tprintf(mp->tcb,closing);
  275.         close_tcp(mp->tcb);
  276.         mail_delete(mp);
  277.         break;
  278.     case RCPT_CMD:    /* Specify recipient */
  279.         if((cp = getname(arg)) == NULLCHAR){
  280.             tprintf(mp->tcb,syntax);
  281.             break;
  282.         }
  283.         /* Strip the @host part; all names must be local */
  284.         if((cp1 = index(cp,'@')) != NULLCHAR)
  285.             *cp1 = '\0';
  286.  
  287.         /* Check to see if we can open the mailbox */
  288.         sprintf(mailbox,MAILSPOOL,cp);
  289. #ifdef    AMIGA
  290.         /* probably could use AMIGA's Lock()/Unlock(), but that
  291.            seems like overkill */
  292.         if((fp = fopen(mailbox,"r")) == NULL){
  293. #else
  294.         if((fp = fopen(mailbox,"a+")) == NULL){
  295. #endif
  296.             tprintf(mp->tcb,badname,cp);
  297.             break;
  298.         }
  299.         fclose(fp);
  300.         /* Allocate an entry on the recipient list. This
  301.          * assembles the list backwards, but what the heck.
  302.          */
  303.         if((ap = (struct addr *)malloc(sizeof(struct addr))) == NULLADDR){
  304.             close_tcp(mp->tcb);
  305.             mail_delete(mp);
  306.             break;
  307.         }
  308.         if((ap->val = malloc((unsigned)strlen(mailbox)+1)) == NULLCHAR){
  309.             free((char *)ap);
  310.             close_tcp(mp->tcb);
  311.             mail_delete(mp);
  312.             break;
  313.         }
  314.         strcpy(ap->val,mailbox);
  315.         ap->next = mp->to;
  316.         mp->to = ap;
  317.         tprintf(mp->tcb,ok);
  318.         break;
  319.     case HELP_CMD:
  320.         tprintf(mp->tcb,help);
  321.         break;
  322.     case DATA_CMD:
  323.         if(mp->to == NULLADDR){
  324.             tprintf(mp->tcb,needrcpt);
  325.             break;
  326.         }
  327.         tcp_output(mp->tcb);    /* Send ACK; disk I/O is slow */
  328.         if((mp->data = tmpfile()) == NULLFILE){
  329.             tprintf(mp->tcb,ioerr);
  330.             break;
  331.         }
  332. #ifdef    DATE
  333.         /* Add timestamp; ctime adds newline */
  334.         time(&t);
  335.         if(mp->system != NULLCHAR)
  336.             fprintf(mp->data,
  337.                 "Received: from %s by %s\n\twith SMTP ; %s",
  338.                 mp->system,
  339.                 hostname,
  340.                 ctime(&t));
  341.         else
  342.             fprintf(mp->data,"Received: %s\n",ctime(&t));
  343. #endif
  344.         if(ferror(mp->data)){
  345.             tprintf(mp->tcb,ioerr);
  346.         } else {
  347.             mp->state = DATA_STATE;
  348.             tprintf(mp->tcb,enter);
  349.         }
  350.         break;
  351.     case RSET_CMD:
  352.         tcb = mp->tcb;
  353.         mail_delete(mp);
  354.         if((mp = mail_create(tcb)) == NULLMAIL){
  355.             close_tcp(tcb);
  356.         } else {
  357.             mp->state = COMMAND_STATE;
  358.             tprintf(mp->tcb,reset);
  359.         }
  360.         break;
  361.     }
  362. }
  363. /* Given a string of the form <user@host>, extract the part inside the
  364.  * brackets and return a pointer to it.
  365.  */
  366. static
  367. char *
  368. getname(cp)
  369. register char *cp;
  370. {
  371.     char *cp1;
  372.     char *index(),*strncpy();
  373.  
  374.     if((cp = index(cp,'<')) == NULLCHAR){
  375.         return NULLCHAR;
  376.     }
  377.     cp++;    /* cp -> first char of name */
  378.     if((cp1 = index(cp,'>')) == NULLCHAR){
  379.         return NULLCHAR;
  380.     }
  381.     *cp1 = '\0';
  382.     return cp;
  383. }
  384. /* Deliver mail to the appropriate mail boxes and delete temp file */
  385. static
  386. void
  387. deliver(mp)
  388. register struct mail *mp;
  389. {
  390.     char *index();
  391.     int c;
  392.     register struct addr *ap;
  393.     register FILE *fp;
  394.  
  395.     for(ap = mp->to;ap != NULLADDR;ap = ap->next){
  396.         fseek(mp->data,0L,0);    /* rewind */
  397.         fp = fopen(ap->val,"a+");
  398.         while((c = getc(mp->data)) != EOF){
  399.             if(putc(c,fp) == EOF)
  400.                 break;
  401.         }
  402.         if(ferror(fp)){
  403.             fclose(fp);
  404.             tprintf(mp->tcb,mboxerr,ap->val);
  405.             fclose(mp->data);
  406.             mp->data = NULLFILE;
  407.             return;
  408.         }
  409.         fclose(fp);
  410.     }
  411.     tprintf(mp->tcb,sent);
  412.     fclose(mp->data);
  413.     mp->data = NULLFILE;
  414. }
  415.